#!/user/bin/env python3 
# -*- coding: utf-8 -*-
"""
    __author__ = "wu"
   Description :根据乘客信息预测他们在Titanic号撞击冰山沉没后能否生存。
"""

import tensorflow as tf
import pandas as pd
import matplotlib.pyplot as plt
from pandas import DataFrame
from tensorflow.keras import models, layers


class Passenger:
    """titanic数据集乘客信息的字段说明
    Survived:0代表死亡，1代表存活【y标签】
    Pclass:乘客所持票类，有三种值(1,2,3) 【转换成onehot编码】
    Name:乘客姓名 【舍去】
    Sex:乘客性别 【转换成bool特征】
    Age:乘客年龄(有缺失) 【数值特征，添加“年龄是否缺失”作为辅助特征】
    SibSp:乘客兄弟姐妹/配偶的个数(整数值) 【数值特征】
    Parch:乘客父母/孩子的个数(整数值)【数值特征】
    Ticket:票号(字符串)【舍去】
    Fare:乘客所持票的价格(浮点数，0-500不等) 【数值特征】
    Cabin:乘客所在船舱(有缺失) 【添加“所在船舱是否缺失”作为辅助特征】
    Embarked:乘客登船港口:S、C、Q(有缺失)【转换成onehot编码，四维度 S,C,Q,nan】
    """

    def __init__(self, data: DataFrame):
        self.survived = data['survived']


def data_analysis(df):
    # label分布情况
    ax = df["Survived"].value_counts().plot(kind="bar", figsize=(12, 8), fontsize=15, rot=0)
    '''df["Survived"].value_counts() =
0    433
1    279
Name: Survived, dtype: int64
    '''
    ax.set_ylabel("Counts", fontsize=15)
    ax.set_xlabel("Survived", fontsize=15)
    plt.show()

    # 年龄分布
    ax = df["Age"].plot(kind="hist", bins=20, color="purple", figsize=(12, 8), fontsize=15)
    ax.set_ylabel("frequency", fontsize=15)
    ax.set_xlabel("Age", fontsize=15)
    plt.show()

    # 年龄和label相关性
    ax = df.query("Survived==0")["Age"].plot(kind="density", figsize=(12, 8), fontsize=15)
    # ？
    df.query("Survived==1")["Age"].plot(kind="density", figsize=(12, 8), fontsize=15)
    ax.set_ylabel("Density", fontsize=15)
    ax.set_xlabel("Age", fontsize=15)
    ax.legend(["Survived==0", "Survived==1"], fontsize=15)
    plt.show()


def preprocessing(df_data):
    df_result = pd.DataFrame()
    # Pclass
    df_Pclass = pd.get_dummies(df_data["Pclass"])
    df_Pclass.columns = ["Pclass_" + str(x) for x in df_Pclass.columns]
    df_result = pd.concat([df_result, df_Pclass], axis=1)

    # Sex
    df_sex = pd.get_dummies(df_data["Sex"])
    df_result = pd.concat([df_result, df_sex], axis=1)

    # Age
    df_result["Age"] = df_data["Age"].fillna(0)
    df_result["Age_null"] = pd.isna(df_data["Age"]).astype("int32")

    # SibSp, Parch, Fare
    df_result["SibSp"] = df_data["SibSp"]
    df_result["Parch"] = df_data["Parch"]
    df_result["Fare"] = df_data["Fare"]

    # Cabin
    df_result["Cabin"] = pd.isna(df_data["Cabin"]).astype("int32")

    # Embarked
    df_embarked = pd.get_dummies(df_data["Embarked"], dummy_na=True)
    df_embarked.columns = ["Embarked_" + str(x) for x in df_embarked.columns]
    df_result = pd.concat([df_result, df_embarked], axis=1)

    return df_result


def build_model():
    # ?
    tf.keras.backend.clear_session() # 清除其他模型
    model = models.Sequential()
    model.add(layers.Dense(20, activation="relu", input_shape=(15,)))
    model.add(layers.Dense(10, activation="relu"))
    model.add(layers.Dense(1, activation="sigmoid"))

    model.summary()
    model.compile(optimizer="adam", loss="binary_crossentropy",
                  metrics=["AUC", "accuracy"])

    return model


def plot_metric(history, metric):
    train_metric = history.history[metric]
    val_metric = history.history["val_" + metric]

    epochs = range(1, len(train_metric) + 1)
    plt.plot(epochs, train_metric, "bo--", label="train_" + metric)
    plt.plot(epochs, val_metric, "ro-", label="val_" + metric)
    plt.title("Training and validation " + metric)
    plt.xlabel("epochs")
    plt.ylabel(metric)
    plt.legend()
    plt.show()


def main():
    dftrain_raw = pd.read_csv(r'..\data\titanic\train.csv')
    dftest_raw = pd.read_csv(r'..\data\titanic\test.csv')
    # 数据分析
    data_analysis(dftrain_raw)
    # 预处理
    x_train, x_test = preprocessing(dftrain_raw), preprocessing(dftest_raw)
    y_train = dftrain_raw["Survived"].values

    print("x_train shape = {}, x_test shape = {}".format(x_train.shape, x_test.shape))
    # 建模 训练
    model = build_model()
    history = model.fit(x_train, y_train, batch_size=16, epochs=10, validation_split=0.3)
    plot_metric(history, "loss")
    plot_metric(history, "AUC")

    # 预测
    predict = model.predict(x_test)

    # 1.keras方法，模型保存及其读取
    # 1.1 同时保存模型结构和权重，读取模型
    model.save(r"..\data\titanic\keras_model.h5")
    model = models.load_model(r"..\data\titanic\keras_model.h5")
    predict = model.predict(x_test)

    # 1.2.1 分别保存模型结构和权重
    json_model = model.to_json()
    model.save_weights(r"..\data\titanic\keras_model_weight.h5")

    # 1.2.2 先恢复模型结构，后加载权重
    model_json = models.model_from_json(json_model)
    model_json.compile(optimizer="adam", loss="binary_crossentropy",
                       metrics=["AUC", "accuracy"])
    model_json.load_weights(r"..\data\titanic\keras_model_weight.h5")
    predict = model_json.predict(x_test)

    # 2.tensotflow方式保存模型
    # 2.1 只保存权重
    model.save_weights(r"..\data\titanic\tf_model_weight.ckpt", save_format="tf")
    # 2.2 同时保存模型结构和权重，具有跨平台性便于部署
    model.save(r"..\data\titanic\tf_model_savedmodel", save_format="tf")

    model = tf.keras.models.load_model(r"..\data\titanic\tf_model_savedmodel")
    predict = model.predict(x_test)

    print(predict.shape, predict[:10])


if __name__ == '__main__':
    main()
